/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Lexer;

/**
 *
 * @author jefoutta
 */
import java.util.*;
import java.util.regex.*;

public class CompilerDefinition {

    private String grammar;
    private boolean error = false;
    private Stack<String> stackError = new Stack();
    private RE anything = new RE("anything");
    private Hashtable<String,Pattern> Characters = new Hashtable();
    private LinkedList<String> charactersIndex = new LinkedList();
    private Hashtable<String,Pattern> Keywords = new Hashtable();
    private LinkedList<String> keywordsIndex = new LinkedList();
    private Hashtable<String,Pattern> Tokens = new Hashtable();
    private LinkedList<String> tokensIndex = new LinkedList();
    private Matcher matcher;


    private RE cocol = new RE("cocol");
    private RE letter = new RE("letter");
    private RE digit = new RE("digit");
    private RE charr = new RE("charr");
    private RE string = new RE("string");
    private RE number = new RE("number");
    private RE ident = new RE("ident");
    private RE scannerSpecification = new RE("scannerSpecification");
    private RE parserSpecification = new RE("parserSpecification");
    private RE characters = new RE("characters");
    private RE setDecl = new RE("setDecl");
    private RE set = new RE("set");
    private RE set1 = new RE("set1");
    private RE basicSet = new RE("basicSet");
    private RE Char = new RE("Char");
    private RE Char1 = new RE("Char1");
    private RE keywords = new RE("keywords");
    private RE keywordDecl = new RE("keywordDec1");
    private RE tokens = new RE("tokens");
    private RE tokenDecl = new RE("tokenDecl");
    private RE tokenExpr = new RE("tokenExpr");
    private RE tokenTerm = new RE("tokenTerm");
    private RE tokenFactor = new RE("tokenFactor");
    private RE symbol = new RE("symbol");
    private RE whiteSpaceDecl = new RE("whiteSpaceDecl");

    public CompilerDefinition(){
        letter.compile("[abcdefghijklmnopqrstuvwxyz]");
        charr.compile("\'([^\'])\'");
        string.compile("\"[^\"]*\"");
        digit.compile("[0123456789]");
        number.compile(digit.regex()+"("+digit.regex()+")*");
        ident.compile(letter.regex()+"("+letter.regex()+"|"+digit.regex()+")*");
        anything.compile("(((.)|(\n))*)");
        //----------------------------ScannerSpecification
        //CHARACTERS
        Char.compile("("+charr.regex()+"|CHR\\("+number.regex()+"\\))");
        Char1.compile("("+Char.regex()+")"+"(\\.\\."+"("+Char.regex()+")"+")?");
        basicSet.compile("("+string.regex()+")"+"|"+"("+ident.regex()+")"+"|"+Char1.regex());
        set.compile("("+basicSet.regex()+")( )*"+"(((\\+)|(\\-))"+"( )*("+basicSet.regex()+"))*");
        setDecl.compile(ident.regex()+"( )*=( )*"+set.regex());
        characters.compile("CHARACTERS(\n)*("+setDecl.regex()+"(\n)*)*");
        //KEYWORDS
        keywordDecl.compile("("+ident.regex()+")( )*=( )*"+"("+string.regex()+")");
        keywords.compile("KEYWORDS(\n)*("+keywordDecl.regex()+"(\n)*)*");
        //TOKENS
        symbol.compile("("+ident.regex()+")|("+string.regex()+")|("+charr.regex()+")");
        tokenFactor.compile("("+symbol.regex()+")|\\(( )*("+tokenTerm.regex()+")( )*\\)|\\[( )*("+tokenExpr.regex()+")( )*\\]|\\{( )*("+tokenExpr.regex()+")( )*\\}");
        //tokenFactor.compile("("+symbol.regex()+")|(\\(( )*"+symbol.regex()+"( )*\\))|(\\[( )*"+symbol.regex()+"( )*\\])|(\\{( )*"+symbol.regex()+"( )*\\})");
        tokenTerm.compile("("+tokenFactor.regex()+")(( )*("+tokenFactor.regex()+"))*");
        tokenExpr.compile("("+tokenTerm.regex()+")( )*((\\|)( )*("+tokenTerm.regex()+")( )*)*");
        tokenDecl.compile("("+ident.regex()+")(( )*=( )*("+tokenExpr.regex()+"))?(( )*EXCEPT KEYWORDS)?");
        tokens.compile("TOKENS(\n)*("+tokenDecl.regex()+"(\n)*)*");

        //----------------------------Cocol
        scannerSpecification.compile("("+characters.regex()+")?(\n)*("+keywords.regex()+")?");
        parserSpecification.compile("parser");
        cocol.compile("COMPILER"+"( )*"+ident.regex()+"(\n)*"+scannerSpecification.regex()+"(\n)*"+parserSpecification.regex()+"(\n)*"+"END"+"( )*"+ident.regex()+"( )*\\.");
    }

    public void cocol(){
        if(grammar.matches("COMPILER"+anything.pattern())){
            grammar = grammar.replaceFirst("COMPILER( )*", "");
            ident();
            scannerSpecification();
        }else{
            stackError.push("error cocol");
            error = true;
        }
    }

    public String ident(){
        if(!error){
            if(grammar.matches(ident.pattern()+anything.pattern())){
                matcher = Pattern.compile("("+ident.pattern()+")"+anything.pattern()).matcher(grammar);
                matcher.matches();
                grammar = grammar.replaceFirst(ident.pattern()+"( )*(\n)*", "");
                return matcher.group(1);
            }else{
                error = true;
                stackError.push("error ident");
            }
        }
        return null;
    }

    public String string(){
        if(!error){
            if(grammar.matches(string.pattern()+anything.pattern())){
                matcher = Pattern.compile("("+string.pattern()+")"+anything.pattern()).matcher(grammar);
                matcher.matches();
                grammar = grammar.replaceFirst(string.pattern()+"( )*(\n)*", "");
                String s = matcher.group(1).substring(1, matcher.group(1).length()-1);
                if (s.contains("("))
                    s = s.replaceAll("\\(", "\\\\(");
                if (s.contains(")"))
                    s = s.replaceAll("\\)", "\\\\)");
                if (s.contains("*"))
                    s = s.replaceAll("\\*", "\\\\*");
                if (s.contains("+"))
                    s = s.replaceAll("\\+", "\\\\+");
                if (s.contains("?"))
                    s = s.replaceAll("\\?", "\\\\?");
                return(s);
            }else{
                error = true;
                stackError.push("error string");
            }
        }
        return null;
    }

    public String charr(){
        if(grammar.matches(charr.pattern()+anything.pattern())){
            matcher = Pattern.compile("("+charr.pattern()+")"+anything.pattern()).matcher(grammar);
            matcher.matches();
            grammar = grammar.replaceFirst(charr.pattern()+"(( )|(\n))*", "");
            return (matcher.group(1));
        }else{
            error = true;
            stackError.push("error char");
        }return null;
    }

    public char Char(){
        if(grammar.matches(Char.pattern()+anything.pattern())){
            matcher = Pattern.compile("("+Char.pattern()+")"+anything.pattern()).matcher(grammar);
            matcher.matches();
            grammar = grammar.replaceFirst(Char.pattern()+"( )*(\n)*", "");
            if(matcher.group(1).matches("CHR"+anything.pattern())){
                matcher = Pattern.compile("(CHR\\()("+number.pattern()+")"+anything.pattern()).matcher(matcher.group(1));
                matcher.matches();
                return((char)Integer.parseInt(matcher.group(2)));
            }return(matcher.group(1).charAt(1));
        }else{
            error = true;
            stackError.push("error Char");
        }return '%';
    }

    public void scannerSpecification(){
        if(!error){
            if(grammar.matches("CHARACTERS"+anything.pattern())){
                grammar = grammar.replaceFirst("CHARACTERS(( )|(\n))*", "");
                while((!grammar.matches("((KEYWORDS)|(TOKENS)|(IGNORE)|(PRODUCTIONS))"+anything.pattern()))&&(!error)){
                    setDecl();
                }
            }
            if(grammar.matches("KEYWORDS"+anything.pattern())){
                grammar = grammar.replaceFirst("KEYWORDS(( )|(\n))*", "");
                while((!grammar.matches("((TOKENS)|(IGNORE)|(PRODUCTIONS))"+anything.pattern()))&&(!error)){
                    keywordDecl();
                }
            }
            if(grammar.matches("TOKENS"+anything.pattern())){
                grammar = grammar.replaceFirst("TOKENS(( )|(\n))*", "");
                while((!grammar.matches("((IGNORE)|(PRODUCTIONS))"+anything.pattern()))&&(!error)){
                    tokenDecl();
                }
            }
        }
    }

    public void setDecl(){
        if(!error){
            String id = ident();
            if(grammar.matches("="+anything.pattern())){
                grammar = grammar.replaceFirst("=( )*", "");
                String regex = "("+set()+")";
                Characters.put(id, Pattern.compile(regex));
                charactersIndex.addLast(id);
                if(grammar.matches("\\."+anything.pattern()))
                    grammar = grammar.replaceFirst("\\.( )*(\n)*", "");
            }else{
                error = true;
                stackError.push("error setDecl");
            }
        }
    }

    public String set(){
        if(!error){
            String s = basicSet();
            while(grammar.matches("((\\+)|(\\-))"+anything.pattern())){
                matcher = Pattern.compile("((\\+)|(\\-))"+anything.pattern()).matcher(grammar);
                matcher.matches();
                grammar = grammar.replaceFirst("((\\+)|(\\-))( )*", "");
                if(matcher.group(1).equals("+")){
                    s = s +"("+ basicSet()+")";
                }else if(matcher.group(1).equals("-")){
                    s = "["+s+"&&"+"[^"+basicSet()+"]"+"]";
                }
            }
            return s;
        }return null;
    }

    public String basicSet(){
        if(!error){
            if(grammar.matches(string.pattern()+anything.pattern()))
                return ("(["+string()+"])");
            else
                if(grammar.matches(ident.pattern()+anything.pattern()))
                    return Characters.get(ident()).pattern();
                else
                    if(grammar.matches(Char.pattern()+anything.pattern())){
                        String s = ""+Char();
                        if(grammar.matches("(\\.\\.)"+Char.pattern()+anything.pattern())){
                            grammar = grammar.replaceFirst("(\\.\\.)", "");
                            s = "(["+s+"-"+Char()+"])";
                        }
                        return s;
                    }else{
                        error = true;
                        stackError.push("basicSet error");
                    }
        }
        return null;
    }

    public void keywordDecl(){
        if(!error){
            String id = ident();
            if(grammar.matches("="+anything.pattern())){
                grammar = grammar.replaceFirst("=( )*", "");
                String regex = "("+string()+")";
                Keywords.put(id, Pattern.compile(regex));
                keywordsIndex.addLast(id);
                if(grammar.matches("\\."+anything.pattern()))
                    grammar = grammar.replaceFirst("\\.( )*(\n)*", "");
            }else{
                error = true;
                stackError.push("keywordDecl error");
            }
        }
    }

    public void tokenDecl(){
        if(!error){
            String id = ident();
            String regex = null;
            if(grammar.matches("(=)"+anything.pattern())){
                grammar = grammar.replaceFirst("=( )*", "");
                regex = tokenExpr();
                
            }
            if(grammar.matches("(EXCEPT KEYWORDS)"+anything.pattern())){
                grammar = grammar.replaceFirst("EXCEPT KEYWORDS( )*", "");
                String substring = "";
                for(int i = 0; i < keywordsIndex.size(); i++)
                    substring = substring +"("+keywordsIndex.get(i)+")";
                substring = substring;
                regex = "["+regex+"[^"+substring+"]]";
            }
            Tokens.put(id, Pattern.compile(regex));
            tokensIndex.addLast(id);
            if(grammar.matches("(\\.)"+anything.pattern())){
                grammar = grammar.replaceFirst("\\.(( )|(\n))*", "");
            }else{
                error = true;
                stackError.push("tokenDecl error");
            }
        }
    }

    public String tokenExpr(){
       if(!error){
           String s = "("+tokenTerm()+")";
           while((!grammar.matches("((EXCEPT KEYWORDS)|(\\))|(\\])|(\\}|(\\.)))"+anything.pattern()))&&(!error)){
               if(grammar.matches("\\|"+anything.pattern())){
                    grammar = grammar.replaceFirst("\\|( )*", "");
                    s = s + "|("+tokenTerm()+")";
               }else{
                   error = true;
                   stackError.push("tokenExpr error");
               }
           }return s;
       }return null;
    }

    public String tokenTerm(){
        if(!error){
            String s = "("+tokenFactor()+")";
            while((!grammar.matches("((EXCEPT KEYWORDS)|(\\))|(\\])|(\\})|(\\.)|(\\|))"+anything.pattern()))&&(!error)){
                s = s + "("+tokenFactor()+")";
            }return s;
        }return null;
    }

    public String tokenFactor(){
        if(!error){
            String s = null;
            if(grammar.matches("("+symbol.pattern()+")"+anything.pattern())){
                s = symbol();
            }else
                if(grammar.matches("\\("+anything.pattern())){
                    grammar = grammar.replaceFirst("\\(( )*", "");
                    s = "("+tokenExpr()+")";
                    if(grammar.matches("\\)"+anything.pattern())){
                        grammar = grammar.replaceFirst("\\)(( )|(\n))*", "");
                    }else{
                        error = true;
                        stackError.push("tokenFactor error");
                    }
                }else
                    if(grammar.matches("\\["+anything.pattern())){
                        grammar = grammar.replaceFirst("\\[( )*", "");
                        s = "(("+tokenExpr()+")?)";
                        if(grammar.matches("\\]"+anything.pattern())){
                            grammar = grammar.replaceFirst("\\](( )|(\n))*", "");
                        }else{
                            error = true;
                            stackError.push("tokenFactor error");
                        }
                    }else
                        if(grammar.matches("\\{"+anything.pattern())){
                            grammar = grammar.replaceFirst("\\{( )*", "");
                            s = "(("+tokenExpr()+")*)";
                            if(grammar.matches("\\}"+anything.pattern())){
                                grammar = grammar.replaceFirst("\\}(( )|(\n))*", "");
                            }else{
                                error = true;
                                stackError.push("tokenFactor error");
                            }
                        }else{
                            error = true;
                            stackError.push("tokenFactor error");
                        }return s;
        }return null;
    }

    public String symbol(){
        if(!error){
            if(grammar.matches(ident.pattern()+anything.pattern())){
                String id = ident();
                if(Characters.containsKey(id))
                    return Characters.get(id).pattern();
                else
                    if(Keywords.containsKey(id))
                        return Keywords.get(id).pattern();
                    else
                        if(Tokens.containsKey(id))
                            return Tokens.get(id).pattern();
            }else
                if(grammar.matches(string.pattern()+anything.pattern())){
                    return string();
                }else
                    if(grammar.matches("("+charr.pattern()+")"+anything.pattern())){
                        return charr();
                    }else{
                        error = true;
                        stackError.push("symbol error");
                    }
        }return null;
    }
    
    public String prueba(String s){
        grammar = s;
        cocol();
        if(!stackError.empty())
            System.out.println(stackError.toString());
        String test = "\'b\'\n"+
                        "PRODU";
        boolean b = true;
        if(b)
            return "si";
        else
            return "no";
    }
}